/***************************************************************************
 *   Copyright (C) 2010 by programmeboy                                    *
 *   programmeboy@gmail.com                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.                                        *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * 
 ***************************************************************************/

#include "InstDrv.h"

#include <tchar.h>
#include <windows.h>
#include <stdlib.h>
#include <string.h>

#include <Shlwapi.h>
#pragma comment (lib, "Shlwapi.lib")


CInstDrv::CInstDrv(void)
{
}

CInstDrv::~CInstDrv(void)
{
}

BOOL 
CInstDrv::InstallDriver( 
    IN SC_HANDLE SchSCManager, 
    IN LPCTSTR DriverName, 
    IN LPCTSTR ServiceExe 
)
/*++

Routine Description:

    Creates a driver service.

Arguments:

	

Return Value:

	None

--*/
{
    SC_HANDLE  schService;

    //
    // NOTE: This creates an entry for a standalone driver. If this
    //       is modified for use with a driver that requires a Tag,
    //       Group, and/or Dependencies, it may be necessary to
    //       query the registry for existing driver information
    //       (in order to determine a unique Tag, etc.).
    //

    schService = CreateService( SchSCManager,          // SCManager database
        DriverName,            // name of service
        DriverName,            // name to display
        SERVICE_ALL_ACCESS,    // desired access
        SERVICE_KERNEL_DRIVER, // service type
        SERVICE_DEMAND_START,  // start type
        SERVICE_ERROR_NORMAL,  // error control type
        ServiceExe,            // service's binary
        NULL,                  // no load ordering group
        NULL,                  // no tag identifier
        NULL,                  // no dependencies
        NULL,                  // LocalSystem account
        NULL                   // no password
        );
    if ( schService == NULL )
        return FALSE;

    CloseServiceHandle( schService );

    return TRUE;
}


BOOL 
CInstDrv::StartDriver( 
    IN SC_HANDLE SchSCManager, 
    IN LPCTSTR DriverName 
)
/*++

Routine Description:

    Starts the driver service.

Arguments:

	

Return Value:

	None

--*/
{
    SC_HANDLE  schService;
    BOOL       ret;

    schService = OpenService( SchSCManager,
        DriverName,
        SERVICE_ALL_ACCESS
        );
    if ( schService == NULL )
        return FALSE;

    ret = StartService( schService, 0, NULL )
        || GetLastError() == ERROR_SERVICE_ALREADY_RUNNING 
        || GetLastError() == ERROR_SERVICE_DISABLED;

    CloseServiceHandle( schService );

    return ret;
}


BOOL 
CInstDrv::OpenDevice( 
    IN LPCTSTR DriverName, 
    OUT HANDLE * lphDevice 
)
/*++

Routine Description:

    Opens the device and returns a handle if desired.

Arguments:

	

Return Value:

	None

--*/
{
    TCHAR    completeDeviceName[64];
    HANDLE   hDevice;

    //
    // Create a \\.\XXX device name that CreateFile can use
    //
    // NOTE: We're making an assumption here that the driver
    //       has created a symbolic link using it's own name
    //       (i.e. if the driver has the name "XXX" we assume
    //       that it used IoCreateSymbolicLink to create a
    //       symbolic link "\DosDevices\XXX". Usually, there
    //       is this understanding between related apps/drivers.
    //
    //       An application might also peruse the DEVICEMAP
    //       section of the registry, or use the QueryDosDevice
    //       API to enumerate the existing symbolic links in the
    //       system.
    //

    if( (GetVersion() & 0xFF) >= 5 ) {

        //
        // We reference the global name so that the application can
        // be executed in Terminal Services sessions on Win2K
        //

        wsprintf( completeDeviceName, TEXT("\\\\.\\Global\\%s"), DriverName );

    } else {

        wsprintf( completeDeviceName, TEXT("\\\\.\\%s"), DriverName );
    }
    hDevice = CreateFile( completeDeviceName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
        );
    if ( hDevice == ((HANDLE)-1) )
        return FALSE;

    //
    // If user wants handle, give it to them.  Otherwise, just close it.
    //

    if ( lphDevice )
        *lphDevice = hDevice;
    else
        CloseHandle( hDevice );

    return TRUE;
}


BOOL 
CInstDrv::StopDriver( 
    IN SC_HANDLE SchSCManager,
    IN LPCTSTR DriverName 
)
/*++

Routine Description:

    Has the configuration manager stop the driver (unload it)

Arguments:

	

Return Value:

	None

--*/
{
    SC_HANDLE       schService;
    BOOL            ret;
    SERVICE_STATUS  serviceStatus;

    schService = OpenService( SchSCManager, DriverName, SERVICE_ALL_ACCESS );
    if ( schService == NULL )
        return FALSE;

    ret = ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus );

    CloseServiceHandle( schService );

    return ret;
}


BOOL 
CInstDrv::RemoveDriver( 
    IN SC_HANDLE SchSCManager, 
    IN LPCTSTR DriverName 
)
/*++

Routine Description:

    Deletes the driver service.

Arguments:

	

Return Value:

	None

--*/
{
    SC_HANDLE  schService;
    BOOL       ret;

    schService = OpenService( SchSCManager,
        DriverName,
        SERVICE_ALL_ACCESS
        );

    if ( schService == NULL )
        return FALSE;

    ret = DeleteService( schService );

    CloseServiceHandle( schService );

    return ret;
}

BOOL 
CInstDrv::UnloadDeviceDriver( 
    IN const TCHAR* Name 
)
/*++

Routine Description:

    Stops the driver and has the configuration manager unload it.

Arguments:

	Name - Driver name

Return Value:

	None

--*/
{
    SC_HANDLE	schSCManager;

    schSCManager = OpenSCManager(	NULL,                 // machine (NULL == local)
                                    NULL,                 // database (NULL == default)
                                    SC_MANAGER_ALL_ACCESS // access required
                                    );

    StopDriver(schSCManager, Name);
    RemoveDriver(schSCManager, Name);
    CloseServiceHandle(schSCManager);

    return TRUE;
}


BOOL 
CInstDrv::LoadDeviceDriver( 
    IN const TCHAR* Name, 
    IN const TCHAR * Path, 
    OUT HANDLE * lphDevice, 
    OUT PDWORD Error 
)
/*++

Routine Description:

    Registers a driver with the system configuration manager 
	and then loads it.

Arguments:

	

Return Value:

	None

--*/
{
    SC_HANDLE	schSCManager;
    BOOL		okay;

    schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

    //
    // Remove previous instance
    //

    RemoveDriver( schSCManager, Name );

    //
    // Ignore success of installation: it may already be installed.
    //

    InstallDriver( schSCManager, Name, Path );

    //
    // Ignore success of start: it may already be started.
    //

    StartDriver( schSCManager, Name );

    //
    // Do make sure we can open it.
    //

    okay = OpenDevice( Name, lphDevice );
    *Error = GetLastError();
    CloseServiceHandle( schSCManager );

    return okay;
}

BOOL 
CInstDrv::GetDriverPath(
    IN LPTSTR lpDrvName, 
    OUT LPTSTR lpFileName, 
    IN DWORD dwSize 
)
{
    TCHAR szPath[MAX_PATH];
    GetModuleFileName( NULL, szPath, MAX_PATH );
    lstrcpy( _tcsrchr( szPath, _T('\\') ) + 1, lpDrvName );

    lstrcpyn( lpFileName, szPath, dwSize );

    return PathFileExists( lpFileName );
}